﻿using Services.OtherServices;
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace GenericWin
{
    /// <summary>
    /// Controller générique mère d'une application. 
    /// Permet de controller l'authentification, les actions des menus.
    /// </summary>
    public abstract class GenericMainController : IMenuAction, IConnexionAuthentification
    {
        #region Attribut(s)

        private static GenericApplication ge;

        #endregion

        #region Propriété(s)

        /// <summary>
        /// Boolean permettant de savoir si le développeur veut implémenter sa propre implémentation de connexion, 
        /// ou si il laisse celle par défaut : Login/Mdp LDAP puis la table générique.
        /// True si il veut implémenter sa connexion, false sinon
        /// </summary>
        public Boolean RedirectionAuthentification
        {
            get;
            set;
        }

        /// <summary>
        /// Retourne l'utilisateur courant de l'application
        /// </summary>
        public static GENERAL_USER CurrentUser
        {
            get;
            private set;
        }

        /// <summary>
        /// Obtient la configuration de l'application
        /// </summary>
        public static Configuration Config
        {
            get
            {
                //if (ge != null && ge.Config == null)
                //    BuildConfiguration();

                if (ge != null && ge.Config != null)
                    return ge.Config;
                else
                    return null;
            }
        }

        #endregion

        #region Constructeur(s)

        /// <summary>
        /// Constructeur sans paramètre. 
        /// RedirectionAuthentification est à false par défaut
        /// </summary>
        public GenericMainController()
        {
            RedirectionAuthentification = false;
        }

        /// <summary>
        /// Constructeur prenant en paramètre un booléan permettant de savoir si il y a redirection d'authentification ou non
        /// </summary>
        /// <param name="redirect"></param>
        public GenericMainController(Boolean redirect)
        {
            RedirectionAuthentification = redirect;
        }

        #endregion

        #region Méthode(s)

        #region Méthodes Abstraites

        /**
         * Ces méthodes doivent être implémentées par le controleur principal qui va hérité de cette classe.
         * Authentificate(..) permet d'implémenter sa propre Authentification avec les valeurs des champs passé en paramètres.
         * DoAction(..) permet de retourner le UserControl qui doit etre affiché lors d'un clic sur l'item du menu passé en paramètre
         * BuildMenu() permet de retourner la List<ItemMenu>, c'est donc sous forme d'arbre. C'est le programmeur qui a la charge de construire cette list 
         * BuildConfiguration() permet de créer une instance d'une classe de type Configuration initialiser par le développeur de l'application
         * InitApplication() permet d'instancier les view models de l'application et de les stocker dans le service dédié à ça. Appelé avant BuildConfiguration.
        **/

        public abstract bool Authentificate(Dictionary<string, string> values);

        public abstract GenericUserControl DoAction(MenuItemViewModel item);

        public abstract List<MenuItemViewModel> BuildMenu();

        public abstract Configuration BuildConfiguration();

        public abstract void InitApplication();

        #endregion

        #region Méthodes virtuelles

        /// <summary>
        /// Permet de construire l'application de base sans aucun menu
        /// </summary>
        /// <param name="numVersion">Version de l'application</param>
        /// <param name="nomApp">Nom de l'application</param>
        /// <param name="env">Environnement</param>
        /// <param name="observer">Observeur des actions sur le menu</param>
        /// <returns>Retourne la vue duement construite</returns>
        public virtual GenericApplication BuildGenericApplication(IMenuAction observer, GENERAL_USER userConnecte)
        {
            return BuildGenericApplication(observer, userConnecte, null);
        }

        /// <summary>
        /// Permet de construire l'application de base. 
        /// Cette méthode peut être redéfinit par le controlleur père de l'application qui hérite de cette classe.
        /// </summary>
        /// <param name="numVersion">Version de l'application</param>
        /// <param name="nomApp">Nom de l'application</param>
        /// <param name="env">Environnement</param>
        /// <param name="observer">Observeur des actions sur le menu</param>
        /// <param name="menus">Menu que la vue doit disposer</param>
        /// <returns>Retourne la vue duement construite</returns>
        public virtual GenericApplication BuildGenericApplication(IMenuAction observer, GENERAL_USER userConnecte, List <MenuItemViewModel> menus)
        {
            InitApplication();
            
            ge = new GenericApplication(menus, observer);
            CurrentUser = userConnecte;
            ge.Config = BuildConfiguration();

            if (ge.Config != null)
            {
                ge.Version = ge.Config.Version;
                ge.NomApplication = ge.Config.NomApplication;
                ge.Environnement = ge.Config.Env;
                ge.PrintDefault.Content = ge.Config.NomImprimanteParDefaut;
                ge.Welcome.Content = ge.Config.WelcomeMessage();
                ge.InstanceBDD.Content = ge.Config.InstanceBDD;
                ge.LoginUserConnect.Content = ge.Config.LoginUserConnecte;
            }

            return ge;
        }


        /// <summary>
        /// Appelé X fois, en fonction du parametre nbEtape de la méthode LongLoading(..)
        /// Doit être surchargé par le controller haut niveau de l'application
        /// </summary>
        /// <param name="w">Splash Screen</param>
        /// <param name="etape">Numéro de l'étape</param>
        /// <param name="nbMaxEtape">Maximum d'étape pour LongLoading</param>
        /// <param name="message">Message de l'étape</param>
        public virtual void LongLoadingApplication(Window w, int etape, int nbMaxEtape, String message) { }

        /// <summary>
        /// Appelé X fois, en fonction du parametre nbEtape de la méthode ShortLoading(..)
        /// Doit être surchargé par le controller haut niveau de l'application
        /// </summary>
        /// <param name="w"></param>
        /// <param name="etape">Numéro de l'étape</param>
        /// <param name="nbMaxEtape">Maximum d'étape pour ShortLoading</param>
        /// <param name="message">Message de l'étape</param>
        public virtual void ShortLoadingApplication(Window w, int etape, int nbMaxEtape, String message) { }

        #endregion

        #region Méthodes Concrétes

        /// <summary>
        /// Utiliser pour déconnecter l'utilisateur de l'application
        /// </summary>
        public void LogOff()
        {
            ge.LogOff(true);
        }

        /// <summary>
        /// Utiliser pour fermer l'application
        /// </summary>
        public void Quit()
        {
            ge.LogOff(false);
        }

        /// <summary>
        /// Méthode appelé lors de l'appel du splashscreen pour effectuer un chargement long.
        /// nbEtape, delay.Length  doivent être égaux
        /// </summary>
        /// <param name="w">Splash Screen passé en paramètre.</param>
        /// <param name="nbEtape">Nombre d'étape du chargement</param>
        /// <param name="delay">Tableau de délai pour chaque étape</param>
        /// <returns>Retourne un délai asynchrone égale à delay[X]</returns>
        public async Task LongLoading(Window w, int nbEtape, Int32[] delay)
        {
            await LongLoading(w, nbEtape, delay, new String[nbEtape]);
        }

        /// <summary>
        /// Méthode appelé lors de l'appel du splashscreen pour effectuer un chargement court.
        /// nbEtape, delay.Length doivent être égaux
        /// </summary>
        /// <param name="w">Splash Screen passé en paramètre.</param>
        /// <param name="nbEtape">Nombre d'étape du chargement</param>
        /// <param name="delay">Tableau de délai pour chaque étape</param>
        /// <returns>Retourne un délai asynchrone égale à delay[X]</returns>
        public async Task ShortLoading(Window w, int nbEtape, Int32[] delay)
        {
            await ShortLoading(w, nbEtape, delay, null);
        }

        /// <summary>
        /// Méthode appelé lors de l'appel du splashscreen pour effectuer un chargement long.
        /// nbEtape, delay.Length et message.Length doivent être égaux
        /// </summary>
        /// <param name="w">Splash Screen passé en paramètre.</param>
        /// <param name="nbEtape">Nombre d'étape du chargement</param>
        /// <param name="delay">Tableau de délai pour chaque étape</param>
        /// <param name="message">Tableau de message associé pour les étapes. Laisser null pour utiliser le message par défaut.</param>
        /// <returns>Retourne un délai asynchrone égale à delay[X]</returns>
        public async Task LongLoading(Window w, int nbEtape, Int32[] delay, String[] message)
        {
            if ((message != null && (nbEtape != delay.Length || nbEtape != message.Length || delay.Length != message.Length)) ||
               ( message == null && nbEtape != delay.Length))
                throw new Exception("Le nombre d'étape est incorrect. Un ou plusieurs arguments n'ont pas les mêmes tailles.");

            for (int i = 0; i < nbEtape; ++i)
            {
                String m = message[i] == null || message[i].Equals(String.Empty) ?
                    String.Format("Etape {0} sur {1}", i + 1, nbEtape) :
                    message[i];

                LongLoadingApplication(w, i, nbEtape, m);
                await Task.Delay(delay[i]);
            }
        }

        /// <summary>
        /// Méthode appelé lors de l'appel du splashscreen pour effectuer un chargement court.
        /// nbEtape, delay.Length et message.Length doivent être égaux
        /// </summary>
        /// <param name="w">Splash Screen passé en paramètre.</param>
        /// <param name="nbEtape">Nombre d'étape du chargement</param>
        /// <param name="delay">Tableau de délai pour chaque étape</param>
        /// <param name="message">Tableau de message associé pour les étapes. Laisser null pour utiliser le message par défaut.</param>
        /// <returns>Retourne un délai asynchrone égale à delay[X]</returns>
        public async Task ShortLoading(Window w, int nbEtape, Int32[] delay, String[] message)
        {
            if ((message != null && (nbEtape != delay.Length || nbEtape != message.Length || delay.Length != message.Length)) ||
               (message == null && nbEtape != delay.Length))
                throw new Exception("Le nombre d'étape est incorrect. Un ou plusieurs arguments n'ont pas les mêmes tailles.");

            for (int i = 0; i < nbEtape; ++i)
            {
                String m = message[i] == null || message[i].Equals(String.Empty) ?
                    String.Format("Etape {0} sur {1}", i + 1, nbEtape) :
                    message[i];

                ShortLoadingApplication(w, i, nbEtape, m);
                await Task.Delay(delay[i]);
            }
        }
        #endregion

        #endregion
    }
}
